﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.Compilation;
using System.Web.Configuration;
using System.Web.UI;



/// <summary>
/// Custom ASP.NET Build Provider for the .jsx file extension.
/// </summary>
namespace AjaxControlFramework.Compilation
{
    [BuildProviderAppliesTo(BuildProviderAppliesTo.All)]
    public class JavaScriptBuildProvider : System.Web.Compilation.BuildProvider
    {
        //------// Properties \\--------------------------------------------\\
        public virtual JsxParser Parser { get; set; }
        public virtual JavaScriptCodeDom CodeDom { get; protected set; }


        private string _fileName = null;
        public virtual string FileName
        {
            get
            {
                if (_fileName == null)
                {
                    if (String.IsNullOrEmpty(base.VirtualPath)) { return null; }

                    _fileName = VirtualPathUtility.GetFileName(base.VirtualPath);
                }

                return _fileName;
            }
        }


        public override CompilerType CodeCompilerType
        {
            get
            {
                string language = "VB"; // Default value.
                
                if (!Parser.ParsingComplete)
                {
                    using (Stream fileStream = System.Web.Hosting.VirtualPathProvider.OpenFile(base.VirtualPath))
                    {
                        using (TextReader reader = new System.IO.StreamReader(fileStream))
                        {
                            CodeDom = Parser.ParseFile(reader, FileName);
                        }
                    }
                }


                if (Parser != null && !String.IsNullOrEmpty(Parser.Language))
                {
                    language = Parser.Language;
                }

                return base.GetDefaultCompilerTypeForLanguage(language);
            }
        }


        protected static Type VirtualPathType = Type.GetType(Properties.Settings.Default.VirtualPath_TypeName, true);


        public delegate object CreateVirtualPathMethod(string path);

        private static CreateVirtualPathMethod _createVirtualPath = null;
        protected static CreateVirtualPathMethod CreateVirtualPath
        {
            get
            {
                if (_createVirtualPath == null)
                {
                    MethodInfo createMethodInfo = VirtualPathType.GetMethod("Create", (BindingFlags.Public | BindingFlags.Static), null, new Type[] { typeof(string) }, null);

                    DynamicMethod dynamicMethod = new DynamicMethod("Create", VirtualPathType, new Type[] { typeof(string) }, true);

                    ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Call, createMethodInfo);
                    ilGenerator.Emit(OpCodes.Ret);

                    _createVirtualPath = ((CreateVirtualPathMethod)(dynamicMethod.CreateDelegate(typeof(CreateVirtualPathMethod))));
                }

                return _createVirtualPath;
            }
        }


        private object _virtualPathObj = null;
        protected object VirtualPathObj
        {
            get
            {
                if (_virtualPathObj == null)
                {
                    _virtualPathObj = CreateVirtualPath(base.VirtualPath);
                }

                return _virtualPathObj;
            }
        }


        public delegate System.Web.Compilation.ExpressionBuilder GetExpressionBuilderMethod(string prefix, object virtualPath);

        private static GetExpressionBuilderMethod _getExpressionBuilder = null;
        protected static GetExpressionBuilderMethod GetExpressionBuilder 
        {
            get 
            {
                if (_getExpressionBuilder == null) 
                {
                    // Initialize and cache the dynamic method delegate to the ASP.NET internal static method "GetExpressionBuilder" of the System.Web.Compilation.ExpressionBuilder class.
                    MethodInfo getExpressionBuilderMethodInfo = typeof(System.Web.Compilation.ExpressionBuilder).GetMethod("GetExpressionBuilder", (BindingFlags.NonPublic | BindingFlags.Static), null, new System.Type[] { typeof(string), VirtualPathType }, null);

                    DynamicMethod dynamicMethod = new DynamicMethod("GetExpressionBuilder", typeof(System.Web.Compilation.ExpressionBuilder), new Type[] { typeof(string), typeof(object) }, true);
                    
                    ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldarg_1);
                    ilGenerator.Emit(OpCodes.Castclass, VirtualPathType);
                    ilGenerator.Emit(OpCodes.Call, getExpressionBuilderMethodInfo);
                    ilGenerator.Emit(OpCodes.Ret);

                    _getExpressionBuilder = ((GetExpressionBuilderMethod)(dynamicMethod.CreateDelegate(typeof(GetExpressionBuilderMethod))));
                }

                return _getExpressionBuilder;
            }
        }


        public delegate BoundPropertyEntry CreateBoundPropertyEntryMethod();

        private static CreateBoundPropertyEntryMethod _createBoundPropertyEntry = null;
        protected static CreateBoundPropertyEntryMethod CreateBoundPropertyEntry
        {
            get
            {
                if (_createBoundPropertyEntry == null)
                {
                    Type boundPropertyEntryType = typeof(BoundPropertyEntry);
                    ConstructorInfo createBoundPropertyConstructorInfo = boundPropertyEntryType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);

                    DynamicMethod dynamicMethod = new DynamicMethod("CreateBoundProperty", boundPropertyEntryType, Type.EmptyTypes, true);
                    
                    ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                    ilGenerator.Emit(OpCodes.Newobj, createBoundPropertyConstructorInfo);
                    ilGenerator.Emit(OpCodes.Ret);

                    _createBoundPropertyEntry = (CreateBoundPropertyEntryMethod)dynamicMethod.CreateDelegate(typeof(CreateBoundPropertyEntryMethod));
                }

                return _createBoundPropertyEntry;
            }
        }


        private Type _inheritedControlType = null;
        public Type InheritedControlType
        {
            get
            {
                if (_inheritedControlType == null)
                {
                    _inheritedControlType = System.Web.Compilation.BuildManager.GetType(CodeDom.InheritedTypeName, false);
                }

                return _inheritedControlType;
            }
        }


        private bool? _isInheritingFromTemplateControl = null;
        public bool IsInheritingFromTemplateControl
        {
            get
            {
                if (!_isInheritingFromTemplateControl.HasValue)
                {
                    if (InheritedControlType == null || (!String.IsNullOrEmpty(CodeDom.InheritedTypeName) && typeof(System.Web.UI.TemplateControl).IsAssignableFrom(InheritedControlType)))
                    {
                        _isInheritingFromTemplateControl = true;
                    }
                    else
                    {
                        _isInheritingFromTemplateControl = false;
                    }
                }

                return _isInheritingFromTemplateControl.Value;
            }
        }


        private string _inheritedMarkupVirtualPath = null;
        public virtual string InheritedMarkupVirtualPath
        {
            get
            {
                if (_inheritedMarkupVirtualPath == null)
                {
                    if (IsInheritingFromTemplateControl)
                    {
                        _inheritedMarkupVirtualPath = base.VirtualPath.Substring(0, base.VirtualPath.ToLower().IndexOf(".jsx"));
                    }
                }

                return _inheritedMarkupVirtualPath;
            }
        }


        private string _inheritedMarkupTypeName = null;
        public virtual string InheritedMarkupTypeName
        {
            get
            {
                if (_inheritedMarkupTypeName == null && InheritedMarkupVirtualPath != null)
                {
                    _inheritedMarkupTypeName = InheritedMarkupVirtualPath.ToLower().Replace('/', '_').Replace('.', '_').Substring(1);
                }

                return _inheritedMarkupTypeName;
            }
        }


        private string _generatedTypeName = null;
        public virtual string GeneratedTypeName
        {
            get
            {
                if (_generatedTypeName == null && InheritedMarkupTypeName != null)
                {
                    _generatedTypeName = InheritedMarkupTypeName + "_jsx";
                }

                return _generatedTypeName;
            }
        }


        private string _generatedFullTypeName = null;
        public virtual string GeneratedFullTypeName
        {
            get
            {
                if (_generatedFullTypeName == null && GeneratedTypeName != null)
                {
                    _generatedFullTypeName = "ASP." + GeneratedTypeName;
                }

                return _generatedFullTypeName;
            }
        }


        private string _generatedFullyQualifiedTypeName = null;
        public virtual string GeneratedFullyQualifiedTypeName
        {
            get
            {
                if (_generatedFullyQualifiedTypeName == null && GeneratedFullTypeName != null)
                {
                    _generatedFullyQualifiedTypeName = GeneratedFullTypeName + ", " + (generatedAssemblyName ?? String.Empty);
                }

                return _generatedFullyQualifiedTypeName;
            }
        }


        private string _fileHandlerTypeName = null;
        protected virtual string FileHandlerTypeName
        {
            get
            {
                if (_fileHandlerTypeName == null && CodeDom != null && CodeDom.ScriptName != null)
                {
                    _fileHandlerTypeName = CodeDom.ScriptName + "FileHandler";
                }

                return _fileHandlerTypeName;
            }
        }


        private string _fileHandlerFullTypeName = null;
        protected virtual string FileHandlerFullTypeName
        {
            get
            {
                if (_fileHandlerFullTypeName == null && FileHandlerTypeName != null)
                {
                    _fileHandlerFullTypeName = "ASP." + FileHandlerTypeName;
                }

                return _fileHandlerFullTypeName;
            }
        }


        private string _fileHandlerFullyQualifiedTypeName = null;
        protected virtual string FileHandlerFullyQualifiedTypeName
        {
            get
            {
                if (_fileHandlerFullyQualifiedTypeName == null && FileHandlerFullTypeName != null)
                {
                    _fileHandlerFullyQualifiedTypeName = FileHandlerFullTypeName + ", " + (generatedAssemblyName ?? String.Empty);
                }

                return _fileHandlerFullyQualifiedTypeName;
            }
        }


        private ArrayList _virtualPathDependencies = null;
        public override ICollection VirtualPathDependencies
        {
            get
            {
                if (_virtualPathDependencies == null)
                {
                    _virtualPathDependencies = new ArrayList();

                    if (InheritedMarkupVirtualPath != null)
                    {
                        _virtualPathDependencies.Add(base.VirtualPath);
                        _virtualPathDependencies.Add(InheritedMarkupVirtualPath);
                    }
                }

                return _virtualPathDependencies;
            }
        }


        private delegate string OutputAssemblyNameProperty(System.Web.Compilation.AssemblyBuilder assemblyBuilder);
        
        private static object OutputAssemblyNameLock = new object();
        private static OutputAssemblyNameProperty _outputAssemblyName = null;
        private static OutputAssemblyNameProperty OutputAssemblyName
        {
            get
            {
                if (_outputAssemblyName == null)
                {
                    lock (OutputAssemblyNameLock)
                    {
                        if (_outputAssemblyName == null)
                        {
                            PropertyInfo outputAssemblyNameProperty = typeof(System.Web.Compilation.AssemblyBuilder).GetProperty("OutputAssemblyName", BindingFlags.Instance | BindingFlags.NonPublic, null, typeof(string), Type.EmptyTypes, null);
                            MethodInfo outputAssemblyNameGetMethodInfo = outputAssemblyNameProperty.GetGetMethod(true);

                            DynamicMethod dynamicMethod = new DynamicMethod("get_OutputAssemblyName", typeof(string), new Type[] { typeof(System.Web.Compilation.AssemblyBuilder) }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Call, outputAssemblyNameGetMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _outputAssemblyName = (OutputAssemblyNameProperty)dynamicMethod.CreateDelegate(typeof(OutputAssemblyNameProperty));
                        }
                    }
                }

                return _outputAssemblyName;
            }
        }





        /* Reflective Properties */

        protected static Type BuildResultCompiledTemplateTypeType = Type.GetType(Properties.Settings.Default.BuildResultCompiledTemplateType_TypeName, true);
        protected static Type FileChangesMonitorType = Type.GetType(Properties.Settings.Default.FileChangesMonitor_TypeName, true);
        protected static Type FileChangeEventHandlerType = Type.GetType(Properties.Settings.Default.FileChangeEventHandler_TypeName, true);
        protected static Type HttpConfigurationSystemType = Type.GetType(Properties.Settings.Default.HttpConfigurationSystem_TypeName, true);
        protected static Type FileChangeEventType = Type.GetType(Properties.Settings.Default.FileChangeEvent_TypeName, true);
        protected static Type FileAttributesDataType = Type.GetType(Properties.Settings.Default.FileAttributesData_TypeName, true);
        protected static Type DirectoryMonitorType = Type.GetType(Properties.Settings.Default.DirectoryMonitor_TypeName, true);


        private static BuildManager _theBuildManager = null;
        public static BuildManager TheBuildManager
        {
            get
            {
                if (_theBuildManager == null)
                {
                    _theBuildManager = (BuildManager)typeof(BuildManager).GetField("_theBuildManager", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
                }

                return _theBuildManager;
            }
        }


        private static HttpRuntime _theRuntime = null;
        public static HttpRuntime TheRuntime
        {
            get
            {
                if (_theRuntime == null)
                {
                    _theRuntime = (HttpRuntime)typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
                }

                return _theRuntime;
            }
        }


        private static object _fileChangesMonitor = null;
        public static object FileChangesMonitor
        {
            get
            {
                if (_fileChangesMonitor == null)
                {
                    _fileChangesMonitor = typeof(HttpRuntime).GetField("_fcm", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(TheRuntime);
                }

                return _fileChangesMonitor;
            }
        }


        private static object _httpConfigurationSystem = null;
        public static object HttpConfigurationSystem
        {
            get
            {
                if (_httpConfigurationSystem == null)
                {
                    _httpConfigurationSystem = HttpConfigurationSystemType.GetField("s_httpConfigSystem", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
                }

                return _httpConfigurationSystem;
            }
        }
        //------\\ Properties //--------------------------------------------//



        //------// Fields \\------------------------------------------------\\
        private CodeTypeDeclaration handlerClass = null;
        private string generatedAssemblyName = null;
        protected static List<string> AlreadyCompiledVirtualPaths = new List<string>();
        //------\\ Fields //------------------------------------------------//



        //------// Constructors \\------------------------------------------\\
        public JavaScriptBuildProvider()
            : base()
        {
            Parser = new JsxParser();
        }
        //------\\ Constructors //------------------------------------------//



        //------// Methods \\-----------------------------------------------\\
        protected virtual System.Web.Compilation.BuildProvider CreateBuildProvider(string virtualPath, ICollection referencedAssemblies, System.Web.Compilation.AssemblyBuilder assemblyBuilder, bool setNoBuildResult)
        {
            try
            {
                MethodInfo createBuildProviderMethodInfo = typeof(BuildManager).GetMethod("CreateBuildProvider", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { VirtualPathType, typeof(System.Web.Configuration.CompilationSection), typeof(ICollection), typeof(bool) }, null);

                System.Web.Configuration.CompilationSection compilationSection = System.Web.Configuration.WebConfigurationManager.GetWebApplicationSection("system.web/compilation") as System.Web.Configuration.CompilationSection;

                MethodInfo createMethodInfo = VirtualPathType.GetMethod("Create", (BindingFlags.Public | BindingFlags.Static), null, new Type[] { typeof(string) }, null);
                object virtualPathObj = createMethodInfo.Invoke(null, new object[] { virtualPath });

                System.Web.Compilation.BuildProvider desiredProvider = (System.Web.Compilation.BuildProvider)createBuildProviderMethodInfo.Invoke(null, new object[] { virtualPathObj, compilationSection, referencedAssemblies, false });

                CompilerType codeCompilerType = desiredProvider.CodeCompilerType; // Force parsing to start.

                ArrayList typeNames = new ArrayList();
                typeNames.Add("ASP." + InheritedMarkupTypeName + ", " + (generatedAssemblyName ?? String.Empty));

                assemblyBuilder.GetType().GetMethod("AddTypeNames", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(ICollection) }, null).Invoke(assemblyBuilder, new object[] { typeNames });
                assemblyBuilder.GetType().GetMethod("AddBuildProvider", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(System.Web.Compilation.BuildProvider) }, null).Invoke(assemblyBuilder, new object[] { desiredProvider });


                if (setNoBuildResult)
                {
                    typeof(System.Web.Compilation.BuildProvider).GetMethod("SetNoBuildResult", BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null).Invoke(desiredProvider, new object[0]);
                }


                return desiredProvider;
            }
            catch (System.Exception exception)
            {
                throw exception;
            }
        }



        public override void GenerateCode(System.Web.Compilation.AssemblyBuilder assemblyBuilder)
        {
            //TextWriter sourceCodeWriter = assemblyBuilder.CreateCodeFile(this);
            generatedAssemblyName = OutputAssemblyName(assemblyBuilder); // Set this private field before it is used elsewhere.

            //if (sourceCodeWriter != null)
            //{
                //using (sourceCodeWriter)
                //{
                    if (IsInheritingFromTemplateControl)
                    {
                        if (!AlreadyCompiledVirtualPaths.Contains(InheritedMarkupVirtualPath))
                        {
                            System.Web.Compilation.BuildProvider desiredProvider = CreateBuildProvider(InheritedMarkupVirtualPath, base.ReferencedAssemblies, assemblyBuilder, true);

                            MethodInfo addBuildProviderDependencyMethodInfo = base.GetType().GetMethod("AddBuildProviderDependency", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(System.Web.Compilation.BuildProvider) }, null);

                            //addBuildProviderDependencyMethodInfo.Invoke(desiredProvider, new object[] { this });
                            addBuildProviderDependencyMethodInfo.Invoke(this, new object[] { desiredProvider });

                            //CompilerType codeCompilerType = desiredProvider.CodeCompilerType; // Force parsing to start.
                            Type baseTemplateBuildProviderType = Type.GetType(Properties.Settings.Default.BaseTemplateBuildProvider_TypeName, true);

                            TemplateParser providerParser = (TemplateParser)baseTemplateBuildProviderType.GetField("_parser", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(desiredProvider);
                            object dependencies = typeof(TemplateParser).GetField("_sourceDependencies", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(providerParser);


                            string inheritedMarkupFileName = Path.GetFileName(InheritedMarkupVirtualPath).ToLower();

                            foreach (string dependency in (ICollection)dependencies)
                            {
                                if (!AlreadyCompiledVirtualPaths.Contains(dependency))
                                {
                                    AlreadyCompiledVirtualPaths.Add(dependency);


                                    // Confer with the BuildManager and check to see if any of these types have been compiled into an assembly yet. 
                                    // If any have, remove that dependency from the parser and add a new AssemblyReference that points that type to 
                                    // the AssemblyBuilder.

                                    //Type dependencyType = BuildManager.GetCompiledType(dependency);
                                    


                                    System.Web.Compilation.BuildProvider dependencyProvider = CreateBuildProvider(dependency, base.ReferencedAssemblies, assemblyBuilder, true);
                                    addBuildProviderDependencyMethodInfo.Invoke(dependencyProvider, new object[] { desiredProvider });
                                }

                                if (Path.GetFileNameWithoutExtension(dependency).ToLower() == inheritedMarkupFileName && !(VirtualPathDependencies as ArrayList).Contains(dependency))
                                {
                                    (VirtualPathDependencies as ArrayList).Add(dependency);
                                }
                            }


                            AlreadyCompiledVirtualPaths.Add(base.VirtualPath);
                            AlreadyCompiledVirtualPaths.Add(InheritedMarkupVirtualPath);
                        }



                        /*
                        object inheritedMarkupVirtualPath = CreateVirtualPath(InheritedMarkupVirtualPath);

                        bool keyFromVPP = false;
                        MethodInfo getCacheKeyFromVirtualPathMethodInfo = typeof(BuildManager).GetMethod("GetCacheKeyFromVirtualPath", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { VirtualPathType, typeof(bool).MakeByRefType() }, null);
                        string cacheKey = (string)getCacheKeyFromVirtualPathMethodInfo.Invoke(null, new object[] { inheritedMarkupVirtualPath, keyFromVPP });

                        Type buildResultType = Type.GetType(Properties.Settings.Default.BuildResult_TypeName, true);
                        object buildResult = typeof(BuildManager).GetMethod("GetBuildResultFromCache", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null).Invoke(null, new object[] { cacheKey });

                        if (buildResult != null)
                        {
                            object memoryCache = typeof(BuildManager).GetField("_memoryCache", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(TheBuildManager);

                            if (memoryCache != null)
                            {
                                Type memoryCacheType = Type.GetType(Properties.Settings.Default.MemoryBuildResultCache_TypeName, true);
                                Type cacheInternalType = Type.GetType(Properties.Settings.Default.CacheInternal_TypeName, true);
                                Type cacheKeyType = Type.GetType(Properties.Settings.Default.CacheKey_TypeName, true);
                                Type buildResultCompiledAssemblyType = Type.GetType(Properties.Settings.Default.BuildResultCompiledAssemblyBase_TypeName, true);

                                MethodInfo removeAssemblyAndCleanupDependenciesMethodInfo = memoryCacheType.GetMethod("RemoveAssemblyAndCleanupDependencies", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { buildResultCompiledAssemblyType }, null);

                                if (removeAssemblyAndCleanupDependenciesMethodInfo != null)
                                {
                                    removeAssemblyAndCleanupDependenciesMethodInfo.Invoke(memoryCache, new object[] { CastBuildResultAsBuildResultCompiledAssemblyBase(buildResult) });
                                }

                                
                                object internalCache = memoryCacheType.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(memoryCache);
                                MethodInfo removeCacheItemMethodInfo = cacheInternalType.GetMethod("Remove", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { cacheKeyType, typeof(CacheItemRemovedReason) }, null);
                                ConstructorInfo cacheKeyConstructorInfo = cacheKeyType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(string), typeof(bool) }, null);

                                if (internalCache != null && removeCacheItemMethodInfo != null && cacheKeyConstructorInfo != null)
                                {
                                    object cacheKeyObject = cacheKeyConstructorInfo.Invoke(new object[] { cacheKey, false });

                                    if (cacheKeyObject != null)
                                    {
                                        removeCacheItemMethodInfo.Invoke(internalCache, new object[] { cacheKeyObject, CacheItemRemovedReason.DependencyChanged });
                                    }
                                }
                                
                            }
                        }
                        */
                        
                        //else
                        //{

                        
                        /*
                            string cacheKey = GetInternalCacheKey(InheritedMarkupVirtualPath);
                            //object generatedType = Type.GetType(Properties.Settings.Default.DelayLoadType_TypeName).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string) }, null).Invoke(new object[] { generatedAssemblyName, GeneratedFullTypeName });
                            object generatedType = Type.GetType(Properties.Settings.Default.DelayLoadType_TypeName).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string) }, null).Invoke(new object[] { String.Empty, CodeDom.InheritedTypeName });

                            object buildResult = BuildResultCompiledTemplateTypeType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(Type) }, null).Invoke(new object[] { generatedType });
                            Type buildResultType = Type.GetType(Properties.Settings.Default.BuildResult_TypeName, true);

                            buildResultType.GetField("_virtualPath", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(buildResult, CreateVirtualPath(InheritedMarkupVirtualPath));

                            MethodInfo cacheBuildResultInternalMethodInfo = typeof(BuildManager).GetMethod("CacheBuildResultInternal", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), buildResultType, typeof(long), typeof(DateTime) }, null);
                            cacheBuildResultInternalMethodInfo.Invoke(TheBuildManager, new object[] { cacheKey, buildResult, (long)0, DateTime.Now.ToUniversalTime() });
                        */




                        
                        Type objectFactoryCodeDomTreeGeneratorType = Type.GetType("System.Web.Compilation.ObjectFactoryCodeDomTreeGenerator, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true);

                        object objectFactoryCodeDom = assemblyBuilder.GetType().GetField("_objectFactoryGenerator", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(assemblyBuilder);
                        CodeTypeDeclaration objectFactoryType = (CodeTypeDeclaration)objectFactoryCodeDomTreeGeneratorType.GetField("_factoryClass", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(objectFactoryCodeDom);

                        foreach (CodeTypeMember member in objectFactoryType.Members)
                        {
                            if (member.Name == "Create_ASP_" + InheritedMarkupTypeName)
                            {
                                CodeMemberMethod method = (CodeMemberMethod)member;

                                method.Statements.Clear();
                                method.Statements.Add(new CodeMethodReturnStatement(new CodeObjectCreateExpression(new CodeTypeReference(GeneratedFullTypeName))));
                            }
                        }
                        
                    }



                    if (String.IsNullOrWhiteSpace(CodeDom.ScriptName))
                    {
                        if (InheritedControlType != null)
                        {
                            CodeDom.ScriptName = InheritedControlType.Name;
                        }
                        else
                        {
                            CodeDom.ScriptName = Path.GetFileNameWithoutExtension(VirtualPath).Replace(".", "_");
                        }
                    }



                    // 1.) Generate the namespace of the dynamic class.
                    CodeNamespace codeNamespace = GenerateNamespace();

                    // 2.) Generate the class declaration.
                    CodeTypeDeclaration codeClass = GenerateClass(InheritedMarkupVirtualPath);
                    codeNamespace.Types.Add(codeClass);

                    CodeConstructor classConstructor = GenerateConstructor(CodeDom);
                    codeClass.Members.Add(classConstructor);

                    // 3.) Generate the "BuildScriptFile" method.
                    CodeMemberMethod buildScriptMethod = GenerateBuildScriptMethod(CodeDom);
                    codeClass.Members.Add(buildScriptMethod);


                    // 4.) Process the "RenderMode" attribute of the <%@ Script %> directive.
                    ProcessRenderMode(CodeDom, CodeDom.ScriptType, out handlerClass);


                    // 5.) Register the HTTP Handler generated while processing the "RenderMode" attribute, if one was generated.
                    if (handlerClass != null)
                    {
                        codeNamespace.Types.Add(handlerClass);
                    }

                    
                    // 5.) Generate the "FrameworkInitialize" method.

                    // Here we check to see if the inherited type exists and has been loaded into the current 
                    // AppDomain. BuildProviders are invoked after the initial compilation of an assembly, so 
                    // ASP.NET components that are not dynamically compiled at runtime (all components that do 
                    // not have a markup file with an associated code-behind file) will already have been 
                    // loaded into the AppDomain by now and will be found here. If the inherited type is NOT found
                    // then it is safe to assume that the all remaining uncompiled components are awaiting 
                    // processing by a BuildProvider. Only type names of classes that derive from 
                    // System.Web.UI.Control are allowed in the "Inherits" attribute of the <%@ Script %> 
                    // directive. Also, take note that the out-of-the-box BuildProviders of ASP.NET that build 
                    // Controls (.ascx and .aspx) work on System.Web.UI.TemplateControl types. The TemplateControl 
                    // class has a protected virtual method called "FrameworkInitialize" that is implemented by 
                    // one of the classes generated by the .ascx and .aspx BuildProviders. This generated class 
                    // derives from the actual control's class, rather than extend it as a partial of the 
                    // control's class. That means the generated class is a level down from the inheritance tree 
                    // of the .ascx/.aspx control's code-behind class. The dynamic class generated by the 
                    // JavaScriptBuildProvider is a partial of the control's class. It fills in the gap of 
                    // overrides of the "FrameworkInitialize" method by overriding and calling the base class's
                    // version. This leaves the class generated by the .ascx/.aspx BuildProvider to call the 
                    // "FrameworkInitialize" method of the class generated by the .jsx BuildProvider which will 
                    // call the "FrameworkInitialize" method of TemplateControl via the "base.FrameworkInitialize()" 
                    // statement.

                    // If the inherited type is found to exist and be loaded into the current AppDomain already, 
                    // the inheritence tree of that type is checked for a derivation from TemplateControl. If 
                    // the referenced type derives from TemplateControl then an override of the 
                    // "FrameworkInitialize" method can be generated.

                    // If the inherited type is found and does NOT derive from TemplateControl then we can assume 
                    // that a custom, non-template control (i.e. DropDownList or Panel) is being referenced. In 
                    // which case the "FrameworkInitialize" method does not exist anywhere in the inheritence tree 
                    // and an override for it should NOT be generated, thus avoiding a compilation error.

                    // This is all assuming that the type name referenced by the "Inherits" attribute in the 
                    // <%@ Script %> directive derives from the System.Web.UI.Control base class. If the type 
                    // does not derive from System.Web.UI.Control a compilation error will occur.

                    // The "FrameworkInitialize" method is invoked after instantiation of EVERY TemplateControl and 
                    // provides reliable place where code can be inserted that registers the .jsx script in the 
                    // JsxScriptRegistry. The JsxScriptRegistry is important for providing the 
                    // AjaxControl.InternalScriptManager a quick lookup for the script to see if a CompiledScript
                    // declaration is valid. This approach also provides a good place for a JsxRenderingHandler 
                    // delegate to be created that will perform the actual rendering of the .jsx content and store 
                    // the delegate in the JsxScriptRegistry.

                    if (IsInheritingFromTemplateControl)
                    {
                        CodeMemberMethod frameworkInitializeMethod = GenerateFrameworkInitializeMethod(CodeDom);
                        codeClass.Members.Add(frameworkInitializeMethod);
                    }


                    // ScriptType - The purpose of this attribute is to control in what form the compiled script is 
                    //              rendered to the page in. There are 3 options: File, Resource or Inline. The "File" 
                    //              option causes the compiled script to be rendered as if it were a normal external  
                    //              script file within a <script> block with its "src" attribute set. The value of 
                    //              the "src" attribute in this case is a psuedo file path that points to an HttpHandler  
                    //              that is compiled dynamically along with the script contents and is registered 
                    //              at runtime into the in-memory, object representation of the web application's 
                    //              web.config file. The "Resource" option causes the compiled script to be rendered 
                    //              as if it were an embedded resource within a <script> block where the file path set 
                    //              in the "src" attribute has the .axd file extension. With this option, the name of  
                    //              the embedded resource script cooresponds to the value provided in the "ScriptName" 
                    //              attribute of the <%@ Script %> directive. The "Inline" options causes the compiled 
                    //              script to be rendered as a normal <script> block whose content will be the rendered 
                    //              entirely within the <script> block's start and end tag, thus not being referenced 
                    //              externally with the "src" attribute.


                    // So the JavaScriptBuildProvider doesn't work in its current state. The remaining hurdle for the 
                    // component is to implement a way to have the control's partial class generated by the JavaScriptBuildProvider be 
                    // compiled into the same assembly as the the partial classes generated by the UserControlBuildProvider or
                    // PageBuildProvider. 


                    CodeCompileUnit compileUnit = new CodeCompileUnit();
                    compileUnit.Namespaces.Add(codeNamespace);

                    assemblyBuilder.AddCodeCompileUnit(this, compileUnit);
                    assemblyBuilder.GenerateTypeFactory(GeneratedFullTypeName);

                    // Since we won't have access to the actual generated Type object until compilation 
                    // is performed, we can create an instance of the DelayLoadType class that will 
                    // represent the generated type for us. Using a DelayLoadType, we can still override 
                    // the entry for the inherited TemplateControl within the BuildManager's internal 
                    // Virtual Path cache.

                    /*
                    BuildManager theBuildManager = (BuildManager)typeof(BuildManager).GetField("_theBuildManager", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
                    //typeof(BuildManager).GetMethod("CompileWebFile", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { VirtualPathType }, null).Invoke(theBuildManager, new object[] { CreateVirtualPath(InheritedMarkupVirtualPath) });

                    object generatedType = Type.GetType(Properties.Settings.Default.DelayLoadType_TypeName).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string) }, null).Invoke(new object[] { generatedAssemblyName, GeneratedFullTypeName });
                    //object generatedType = Type.GetType(Properties.Settings.Default.DelayLoadType_TypeName).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string) }, null).Invoke(new object[] { generatedAssemblyName, InheritedMarkupTypeName });

                    if (IsInheritingFromTemplateControl)
                    {
                        object inheritedMarkupVirtualPath = CreateVirtualPath(InheritedMarkupVirtualPath);

                        bool keyFromVPP = false;
                        MethodInfo getCacheKeyFromVirtualPathMethodInfo = typeof(BuildManager).GetMethod("GetCacheKeyFromVirtualPath", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { VirtualPathType, typeof(bool).MakeByRefType() }, null);
                        string cacheKey = (string)getCacheKeyFromVirtualPathMethodInfo.Invoke(null, new object[] { inheritedMarkupVirtualPath, keyFromVPP });

                        Type buildResultType = Type.GetType(Properties.Settings.Default.BuildResult_TypeName, true);
                        object buildResult = typeof(BuildManager).GetMethod("GetBuildResultFromCache", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null).Invoke(null, new object[] { cacheKey });

                        MethodInfo addVirtualPathDependenciesMethodInfo = buildResultType.GetMethod("AddVirtualPathDependencies", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(ICollection) }, null);

                        //if (buildResult != null)
                        //{
                            //ArrayList dependencies = new ArrayList();
                            //dependencies.Add(this.VirtualPath);

                            //addVirtualPathDependenciesMethodInfo.Invoke(buildResult, new object[] { dependencies });
                        //}
                        //else
                        if (buildResult == null)
                        {
                            Type buildResultCompiledTemplateTypeType = Type.GetType(Properties.Settings.Default.BuildResultCompiledTemplateType_TypeName, true);
                            buildResult = buildResultCompiledTemplateTypeType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(Type) }, null).Invoke(new object[] { generatedType });

                            buildResultType.GetField("_virtualPath", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(buildResult, CreateVirtualPath(InheritedMarkupVirtualPath));

                            //ArrayList dependencies = new ArrayList();
                            //dependencies.Add(this.VirtualPath);

                            //addVirtualPathDependenciesMethodInfo.Invoke(buildResult, new object[] { dependencies });

                            MethodInfo cacheBuildResultInternalMethodInfo = typeof(BuildManager).GetMethod("CacheBuildResultInternal", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), buildResultType, typeof(long), typeof(DateTime) }, null);
                            cacheBuildResultInternalMethodInfo.Invoke(theBuildManager, new object[] { cacheKey, buildResult, (long)0, DateTime.Now.ToUniversalTime() });
                        }
                    */


                        
                        /*Type buildResultCompiledTemplateTypeType = Type.GetType(Properties.Settings.Default.BuildResultCompiledTemplateType_TypeName, true);
                        object buildResult = buildResultCompiledTemplateTypeType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(Type) }, null).Invoke(new object[] { generatedType });

                        MethodInfo cacheBuildResultInternalMethodInfo = typeof(BuildManager).GetMethod("CacheBuildResultInternal", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), buildResultType, typeof(long), typeof(DateTime) }, null);
                        cacheBuildResultInternalMethodInfo.Invoke(theBuildManager, new object[] { cacheKey, buildResult, (long)0, DateTime.Now.ToUniversalTime() });*/
                    /*}


                    */
                //}

            
                // Force early compilation, get the generated Type instance and invoke the code of the GetGeneratedType method.
                //CompilerResults compilerResults = (CompilerResults)assemblyBuilder.GetType().GetMethod("Compile", BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null).Invoke(assemblyBuilder, null);
                //GetGeneratedType(compilerResults);

                //assemblyBuilder.GetType().GetField("_sourceFiles", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(assemblyBuilder, Type.GetType(Properties.Settings.Default.StringSet_TypeName).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null).Invoke(null));
                //assemblyBuilder.GetType().GetField("_embeddedResourceFiles", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(assemblyBuilder, Type.GetType(Properties.Settings.Default.StringSet_TypeName).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null).Invoke(null));
                // reset the private StringSet _sourceFiles member. - reinstantiate
                // reset the private StringSet _embeddedResourceFiles member. - reinstantiate

            //}
        }


        protected delegate object CastDynamicType(object obj);
        protected static CastDynamicType CastBuildResultAsBuildResultCompiledAssemblyBaseDelegate = null;

        protected virtual object CastBuildResultAsBuildResultCompiledAssemblyBase(object buildResult)
        {
            if (CastBuildResultAsBuildResultCompiledAssemblyBaseDelegate == null)
            {
                Type buildResultCompiledAssemblyType = Type.GetType(Properties.Settings.Default.BuildResultCompiledAssemblyBase_TypeName, true);
                DynamicMethod dynamicMethod = new DynamicMethod("CastBuildResultAsBuildResultCompiledAssemblyBase", typeof(object), new Type[] { typeof(object) }, true);

                ILGenerator ilGenerator = dynamicMethod.GetILGenerator();

                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Castclass, buildResultCompiledAssemblyType);
                ilGenerator.Emit(OpCodes.Ret);

                CastBuildResultAsBuildResultCompiledAssemblyBaseDelegate = (CastDynamicType)dynamicMethod.CreateDelegate(typeof(CastDynamicType));
            }

            return CastBuildResultAsBuildResultCompiledAssemblyBaseDelegate(buildResult);
        }


        protected virtual CodeNamespace GenerateNamespace()
        {
            CodeNamespace namespaceSection = new CodeNamespace("ASP");

            namespaceSection.Imports.Add(new CodeNamespaceImport("System"));
            namespaceSection.Imports.Add(new CodeNamespaceImport("System.IO"));
            namespaceSection.Imports.Add(new CodeNamespaceImport("System.Text"));
            namespaceSection.Imports.Add(new CodeNamespaceImport("System.Web"));
            namespaceSection.Imports.Add(new CodeNamespaceImport("AjaxControlFramework"));
            namespaceSection.Imports.Add(new CodeNamespaceImport("AjaxControlFramework.Compilation"));

            return namespaceSection;
        }


        protected virtual CodeTypeDeclaration GenerateClass(string inheritedTypeName)
        {
            CodeTypeDeclaration codeClass = new CodeTypeDeclaration(GeneratedTypeName);

            codeClass.IsClass = true;
            codeClass.IsPartial = true;
            codeClass.TypeAttributes = TypeAttributes.Public;

            //codeClass.BaseTypes.Add(new CodeTypeReference("global::ASP." + InheritedMarkupTypeName));
            codeClass.BaseTypes.Add(new CodeTypeReference(InheritedMarkupTypeName));
            codeClass.BaseTypes.Add(new CodeTypeReference(typeof(IEmitsJsxScript)));

            return codeClass;
        }


        protected virtual CodeConstructor GenerateConstructor(JavaScriptCodeDom javascriptCodeDom)
        {
            CodeConstructor classConstructor = new CodeConstructor();

            classConstructor.Attributes = MemberAttributes.Public;

            return classConstructor;
        }


        protected virtual CodeCompileUnit GenerateTypeForwardedAttribute()
        {
            CodeCompileUnit assemblyAttributeUnit = new CodeCompileUnit();

            CodeAttributeArgument[] attributeArguments = new CodeAttributeArgument[1];
            attributeArguments[0] = new CodeAttributeArgument("Destination", new CodePrimitiveExpression(CodeDom.InheritedTypeName));

            CodeAttributeDeclaration customAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(System.Runtime.CompilerServices.TypeForwardedToAttribute)), attributeArguments);

            assemblyAttributeUnit.AssemblyCustomAttributes.Add(customAttribute);
            assemblyAttributeUnit.ReferencedAssemblies.Add("System.dll");
            assemblyAttributeUnit.ReferencedAssemblies.Add("System.Web.dll");
            assemblyAttributeUnit.ReferencedAssemblies.Add("AjaxControl.dll");

            //CodeAttributeArgumentCollection attributeArguments = new CodeAttributeArgumentCollection();
            //attributeArguments.Add(new CodeAttributeArgument("FileName", new CodeSnippetExpression("\"" + FileName + "\"")));
            //attributeArguments.Add(new CodeAttributeArgument("Inherits", new CodeSnippetExpression("\"" + CodeDom.InheritedTypeName + "\"")));
            //attributeArguments.Add(new CodeAttributeArgument("ScriptName", new CodeSnippetExpression("\"" + CodeDom.ScriptName + "\"")));
            //attributeArguments.Add(new CodeAttributeArgument("ScriptType", new CodeSnippetExpression("AjaxControl.ScriptType." + CodeDom.ScriptType.ToString())));
            //attributeArguments.Add(new CodeAttributeArgument("Singleton", new CodeSnippetExpression(CodeDom.Singleton ? "true" : "false")));
            //attributeArguments.Add(new CodeAttributeArgument("Language", new CodeSnippetExpression("\"" + CodeDom.Language + "\"")));

            return assemblyAttributeUnit;
        }


        /*
        protected virtual CodeCompileUnit GenerateCompiledJsxScriptAttribute()
        {
            CodeCompileUnit assemblyAttributeUnit = new CodeCompileUnit();

            CodeAttributeArgument[] attributeArguments = new CodeAttributeArgument[6];
            attributeArguments[0] = new CodeAttributeArgument("FileName", new CodePrimitiveExpression(FileName));
            attributeArguments[1] = new CodeAttributeArgument("Inherits", new CodePrimitiveExpression(CodeDom.InheritedTypeName));
            attributeArguments[2] = new CodeAttributeArgument("ScriptName", new CodePrimitiveExpression(CodeDom.ScriptName));
            attributeArguments[3] = new CodeAttributeArgument("ScriptType", new CodeSnippetExpression("AjaxControl.ScriptType." + CodeDom.ScriptType.ToString()));
            attributeArguments[4] = new CodeAttributeArgument("Singleton", new CodePrimitiveExpression(CodeDom.Singleton));
            attributeArguments[5] = new CodeAttributeArgument("Language", new CodePrimitiveExpression(CodeDom.Language));

            CodeAttributeDeclaration customAttribute = new CodeAttributeDeclaration(new CodeTypeReference(typeof(CompiledJsxScriptAttribute)), attributeArguments);

            assemblyAttributeUnit.AssemblyCustomAttributes.Add(customAttribute);
            assemblyAttributeUnit.ReferencedAssemblies.Add("System.dll");
            assemblyAttributeUnit.ReferencedAssemblies.Add("System.Web.dll");
            assemblyAttributeUnit.ReferencedAssemblies.Add("AjaxControl.dll");

            //CodeAttributeArgumentCollection attributeArguments = new CodeAttributeArgumentCollection();
            //attributeArguments.Add(new CodeAttributeArgument("FileName", new CodeSnippetExpression("\"" + FileName + "\"")));
            //attributeArguments.Add(new CodeAttributeArgument("Inherits", new CodeSnippetExpression("\"" + CodeDom.InheritedTypeName + "\"")));
            //attributeArguments.Add(new CodeAttributeArgument("ScriptName", new CodeSnippetExpression("\"" + CodeDom.ScriptName + "\"")));
            //attributeArguments.Add(new CodeAttributeArgument("ScriptType", new CodeSnippetExpression("AjaxControl.ScriptType." + CodeDom.ScriptType.ToString())));
            //attributeArguments.Add(new CodeAttributeArgument("Singleton", new CodeSnippetExpression(CodeDom.Singleton ? "true" : "false")));
            //attributeArguments.Add(new CodeAttributeArgument("Language", new CodeSnippetExpression("\"" + CodeDom.Language + "\"")));

            return assemblyAttributeUnit;
        }
        */


        protected virtual CodeMemberMethod GenerateFrameworkInitializeMethod(JavaScriptCodeDom javascriptCodeDom)
        {
            // 1.) Create the CodeDOM method.
            CodeMemberMethod method = new CodeMemberMethod();

            method.Name = "FrameworkInitialize";
            method.Attributes = MemberAttributes.Override | MemberAttributes.Family; // protected override
            method.ReturnType = new CodeTypeReference(typeof(void));


            // 2.) Create a CodeDOM reference to all of the object types to be used.
            CodeTypeReference templateControlType = new CodeTypeReference(typeof(System.Web.UI.TemplateControl));
            CodeTypeReference jsxScriptType = new CodeTypeReference(typeof(JsxScript));
            CodeTypeReference jsxScriptRegistryType = new CodeTypeReference(typeof(JsxScriptRegistry));
            CodeTypeReference scriptTypeType = new CodeTypeReference(typeof(ScriptType));
            CodeTypeReference renderLocationType = new CodeTypeReference(typeof(JsxRenderLocation));
            CodeTypeReference stringListType = new CodeTypeReference(typeof(List<string>));


            // 3.) Create an instance of the JsxScript object.
            CodeVariableDeclarationStatement newJsxScriptObject = CreateObjectVariable(jsxScriptType, "jsxScript");
            method.Statements.Add(newJsxScriptObject);
            
            CodeVariableReferenceExpression jsxScript = new CodeVariableReferenceExpression("jsxScript");


            // 4.) Set all of the properties of the JsxScript instance.
            CodePropertyReferenceExpression fileNameProperty = new CodePropertyReferenceExpression(jsxScript, "FileName");
            method.Statements.Add(new CodeAssignStatement(fileNameProperty, new CodePrimitiveExpression(VirtualPath)));

            CodePropertyReferenceExpression inheritsProperty = new CodePropertyReferenceExpression(jsxScript, "Inherits");
            method.Statements.Add(new CodeAssignStatement(inheritsProperty, new CodePrimitiveExpression(javascriptCodeDom.InheritedTypeName)));

            CodePropertyReferenceExpression scriptNameProperty = new CodePropertyReferenceExpression(jsxScript, "ScriptName");
            method.Statements.Add(new CodeAssignStatement(scriptNameProperty, new CodePrimitiveExpression(javascriptCodeDom.ScriptName)));

            CodePropertyReferenceExpression scriptTypeProperty = new CodePropertyReferenceExpression(jsxScript, "ScriptType");
            method.Statements.Add(new CodeAssignStatement(scriptTypeProperty, new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(scriptTypeType), javascriptCodeDom.ScriptType.ToString())));

            CodePropertyReferenceExpression singletonProperty = new CodePropertyReferenceExpression(jsxScript, "Singleton");
            method.Statements.Add(new CodeAssignStatement(singletonProperty, new CodePrimitiveExpression(javascriptCodeDom.Singleton)));

            CodePropertyReferenceExpression enabledProperty = new CodePropertyReferenceExpression(jsxScript, "Enabled");
            method.Statements.Add(new CodeAssignStatement(enabledProperty, new CodePrimitiveExpression(javascriptCodeDom.Enabled)));

            CodePropertyReferenceExpression languageProperty = new CodePropertyReferenceExpression(jsxScript, "Language");
            method.Statements.Add(new CodeAssignStatement(languageProperty, new CodePrimitiveExpression(javascriptCodeDom.Language)));


            // Automatically add AJAX Control script dependencies.
            if (InheritedControlType != null)
            {
                if (typeof(IAjaxControl).IsAssignableFrom(InheritedControlType))
                {
                    javascriptCodeDom.Dependencies.Add("AjaxControl");
                    javascriptCodeDom.Dependencies.Add(InheritedControlType.FullName);
                }
            }


            CodePropertyReferenceExpression dependenciesProperty = new CodePropertyReferenceExpression(jsxScript, "Dependencies");
            method.Statements.Add(new CodeAssignStatement(dependenciesProperty, new CodeObjectCreateExpression(stringListType)));

            foreach (string dependency in javascriptCodeDom.Dependencies)
            {
                method.Statements.Add(new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(stringListType), "Add", new CodePrimitiveExpression(dependency)));
            }


            CodePropertyReferenceExpression renderLocationProperty = new CodePropertyReferenceExpression(jsxScript, "RenderLocation");
            method.Statements.Add(new CodeAssignStatement(renderLocationProperty, new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(renderLocationType), javascriptCodeDom.RenderLocation.ToString())));

            if (handlerClass != null)
            {
                CodePropertyReferenceExpression handlerTypeProperty = new CodePropertyReferenceExpression(jsxScript, "HandlerType");
                method.Statements.Add(new CodeAssignStatement(handlerTypeProperty, new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("System.Web.Compilation.BuildManager"), "GetType", new CodePrimitiveExpression(FileHandlerFullyQualifiedTypeName), new CodePrimitiveExpression(false))));
            }

            CodePropertyReferenceExpression instanceProperty = new CodePropertyReferenceExpression(jsxScript, "Instance");
            method.Statements.Add(new CodeAssignStatement(instanceProperty, new CodeThisReferenceExpression()));


            // 5.) Create method reference to the "Add" static method of the JsxScriptRegistry class.
            CodeMethodInvokeExpression containsMethod = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(JsxScriptRegistry)), "Contains", new CodePropertyReferenceExpression(jsxScript, "ScriptName"));

            CodeBinaryOperatorExpression containsScript = new CodeBinaryOperatorExpression(containsMethod, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));

            CodeConditionStatement ifNotContains = new CodeConditionStatement();
            ifNotContains.Condition = containsScript;
            ifNotContains.TrueStatements.Add(new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(JsxScriptRegistry)), "Add", jsxScript));

            method.Statements.Add(ifNotContains);

            
            if (javascriptCodeDom.AutoRender)
            {
                method.Statements.Add(new CodeMethodInvokeExpression(
                    new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(new CodeTypeReference(typeof(InternalScriptManager))), "RegisterCompiledScript"),
                    new CodePropertyReferenceExpression(jsxScript, "ScriptName"),
                    new CodePropertyReferenceExpression(jsxScript, "FileName"),
                    new CodeCastExpression(new CodeTypeReference(typeof(ScriptRenderLocation)), new CodePropertyReferenceExpression(jsxScript, "RenderLocation")),
                    new CodePropertyReferenceExpression(jsxScript, "ScriptType"),
                    new CodePropertyReferenceExpression(jsxScript, "Enabled"),
                    new CodePropertyReferenceExpression(jsxScript, "Dependencies")
                ));
            }


            // 6.) Create method reference to base "FrameworkInitialize" method.
            CodeMethodReferenceExpression frameworkInitializeMethod = new CodeMethodReferenceExpression(new CodeBaseReferenceExpression(), "FrameworkInitialize");
            method.Statements.Add(new CodeMethodInvokeExpression(frameworkInitializeMethod));


            return method;
        }


        protected virtual CodeMemberMethod GenerateBuildScriptMethod(JavaScriptCodeDom javascriptCodeDom)
        {
            // 1.) Create the CodeDOM method.
            CodeMemberMethod method = new CodeMemberMethod();

            // 2.) Create a CodeDOM reference to all of the object types to be used.
            CodeTypeReference stringBuilderType = new CodeTypeReference(typeof(System.Text.StringBuilder));
            CodeTypeReference expressionBuilderType = new CodeTypeReference(typeof(System.Web.Compilation.ExpressionBuilder));
            CodeTypeReference javascriptBuildProviderType = new CodeTypeReference(typeof(JavaScriptBuildProvider));
            CodeTypeReference stringType = new CodeTypeReference(typeof(System.String));

            method.Name = "BuildScript";
            method.Attributes = MemberAttributes.Public;
            method.ReturnType = stringBuilderType;
            
            // 3.) Create a "scriptBuilder" StringBuilder variable.
            CodeVariableDeclarationStatement scriptBuilderVariable = CreateObjectVariable(stringBuilderType, "scriptBuilder");
            method.Statements.Add(scriptBuilderVariable); // Add the statement to the method.

            // 4.) Create a CodeDOM reference to the "scriptBuilder" variable for later use.
            CodeVariableReferenceExpression scriptBuilderReference = new CodeVariableReferenceExpression("scriptBuilder");

            // 5.) Create a CodeDOM reference to the "Append" method of the StringBuilder class.
            CodeMethodReferenceExpression appendMethodReference = new CodeMethodReferenceExpression(scriptBuilderReference, "Append");

            // 6.) Loop through all of the SourceElements of the JavaScriptCodeDom object generated by the parser, 
            // building out the content of the "BuildScriptFile" method as it goes.
            foreach (List<JavaScriptSourceElement> sourceElementList in javascriptCodeDom.SourceElements)
            {
                if (sourceElementList == null) { continue; }

                foreach (JavaScriptSourceElement sourceElement in sourceElementList)
                {
                    CodeStatement newStatement = null;
                    if (sourceElement.Type == JavaScriptSourceElementType.EndOfFile) { break; }

                    switch (sourceElement.Type)
                    {
                        case JavaScriptSourceElementType.Literal:

                            newStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(appendMethodReference, new CodeSnippetExpression("\"" + sourceElement.SourceCode + "\"")));
                            break;

                        case JavaScriptSourceElementType.EvalExpression:
                        case JavaScriptSourceElementType.DataBindingExpression:

                            newStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(appendMethodReference, new CodeSnippetExpression(sourceElement.SourceCode)));
                            break;

                        case JavaScriptSourceElementType.SnippetExpression:

                            newStatement = new CodeSnippetStatement(sourceElement.SourceCode);
                            break;

                        case JavaScriptSourceElementType.CodeExpression:

                            newStatement = new CodeExpressionStatement(new CodeMethodInvokeExpression(appendMethodReference, CreateCodeExpressionCodeExpression(sourceElement.Prefix, sourceElement.SourceCode)));
                            break;

                        default:

                            continue;
                    }


                    newStatement.LinePragma = new CodeLinePragma(FileName, sourceElement.LineNumber);
                    method.Statements.Add(newStatement);
                }
            }

            method.Statements.Add(new CodeMethodReturnStatement(scriptBuilderReference));

            return method;
        }


        protected virtual CodeVariableDeclarationStatement CreateObjectVariable(CodeTypeReference variableType, string variableName)
        {
            CodeVariableDeclarationStatement variable = new CodeVariableDeclarationStatement(variableType, variableName);
            
            CodeObjectCreateExpression variableCreateExpression = new CodeObjectCreateExpression();
            variableCreateExpression.CreateType = variableType;
            
            variable.InitExpression = variableCreateExpression;

            return variable;
        }


        /*
        // Creates a CodeDOM representation of the VirtualPathType property that gets a Type object of the internal 
        // System.Web.VirtualPath type.
        protected virtual CodeMemberField CreateVirtualPathTypeField()
        {
            CodeMemberField field = new CodeMemberField();

            field.Name = "VirtualPathType";
            field.Type = new CodeTypeReference(typeof(System.Type));
            field.Attributes = MemberAttributes.Private | MemberAttributes.Static;

            field.InitExpression = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(System.Type)), "GetType", new CodePrimitiveExpression("System.Web.VirtualPath, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), new CodePrimitiveExpression(true));

            return field;
        }


        protected virtual CodeMemberField CreateVirtualPathObjField()
        {
            CodeMemberField field = new CodeMemberField();

            field.Name = "_virtualPathObj";
            field.Type = new CodeTypeReference("object");
            field.Attributes = MemberAttributes.Private;

            field.InitExpression = new CodePrimitiveExpression(null);

            return field;
        }


        protected virtual CodeMemberProperty CreateVirtualPathObjProperty()
        {
            string generatedTypeName = DetermineReferencedTypeName();

            CodeMemberProperty property = new CodeMemberProperty();

            property.Name = "VirtualPathObj";
            property.Type = new CodeTypeReference("object");
            property.Attributes = MemberAttributes.Family; // protected
            property.HasGet = true;
            property.HasSet = false;

            CodeFieldReferenceExpression privateField = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_virtualPathObj");
            CodeBinaryOperatorExpression privateFieldIsNull = new CodeBinaryOperatorExpression(privateField, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(null));

            // Build out the true statements of the conditional.
            CodeVariableDeclarationStatement methodInfoVariable = new CodeVariableDeclarationStatement(typeof(System.Reflection.MethodInfo), "createMethodInfo");
            methodInfoVariable.InitExpression = new CodeMethodInvokeExpression(
                new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(generatedTypeName), "VirtualPathType"),
                "GetMethod",
                new CodePrimitiveExpression("Create"),
                new CodeBinaryOperatorExpression(new CodeSnippetExpression("System.Reflection.BindingFlags.Public"), CodeBinaryOperatorType.BitwiseOr, new CodeSnippetExpression("System.Reflection.BindingFlags.Static")),
                new CodePrimitiveExpression(null),
                new CodeArrayCreateExpression(typeof(System.Type), new CodeExpression[] { new CodeTypeOfExpression(typeof(System.String)) }),
                new CodePrimitiveExpression(null));

            CodeAssignStatement assignPrivateField = new CodeAssignStatement(
                new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(generatedTypeName), "_createVirtualPath"),
                new CodeMethodInvokeExpression(
                    new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("createMethodInfo"), "Invoke"),
                    new CodeVariableReferenceExpression("createMethodInfo"), new CodePrimitiveExpression(base.VirtualPath)));


            // Build the conditional.
            CodeConditionStatement ifNull = new CodeConditionStatement();
            ifNull.Condition = privateFieldIsNull;
            ifNull.TrueStatements.Add(methodInfoVariable);
            ifNull.TrueStatements.Add(assignPrivateField);

            CodeMethodReturnStatement returnPrivateField = new CodeMethodReturnStatement(privateField);

            property.GetStatements.Add(ifNull);
            property.GetStatements.Add(returnPrivateField);

            return property;
        }


        // Creates a CodeDOM representation of the GetExpressionBuilderMethod delegate declaration.
        protected virtual CodeTypeDelegate CreateGetExpressionBuilderMethodDelegate()
        {
            CodeTypeDelegate delegateMethod = new CodeTypeDelegate("GetExpressionBuilderMethod");

            delegateMethod.ReturnType = new CodeTypeReference(typeof(System.Web.Compilation.ExpressionBuilder));
            delegateMethod.Attributes = MemberAttributes.Private;
            delegateMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(System.String)), "prefix"));
            delegateMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(System.Object)), "virtualPath"));

            return delegateMethod;
        }


        protected virtual CodeMemberField CreateGetExpressionBuilderField()
        {
            CodeMemberField field = new CodeMemberField();

            field.Name = "_getExpressionBuilder";
            field.Type = new CodeTypeReference("GetExpressionBuilderMethod");
            field.Attributes = MemberAttributes.Private | MemberAttributes.Static;

            field.InitExpression = new CodePrimitiveExpression(null);

            return field;
        }


        protected virtual CodeMemberProperty CreateGetExpressionBuilderProperty()
        {
            string generatedTypeName = DetermineReferencedTypeName();

            CodeMemberProperty property = new CodeMemberProperty();

            property.Name = "GetExpressionBuilder";
            property.Type = new CodeTypeReference("GetExpressionBuilderMethod"); // Refactor
            property.Attributes = MemberAttributes.Private | MemberAttributes.Static;
            property.HasGet = true;
            property.HasSet = false;

            CodeFieldReferenceExpression privateField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(generatedTypeName), "_getExpressionBuilder"); // Reference to static field.
            CodeBinaryOperatorExpression privateFieldIsNull = new CodeBinaryOperatorExpression(privateField, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(null));

            // Build out the true statements of the conditional.
            CodeCommentStatement methodInfoComment = new CodeCommentStatement("Initialize and cache the dynamic method delegate to the ASP.NET internal static method \"GetExpressionBuilder\" of the System.Web.Compilation.ExpressionBuilder class.");
            CodeVariableDeclarationStatement methodInfoVariable = new CodeVariableDeclarationStatement(typeof(System.Reflection.MethodInfo), "getExpressionBuilderMethodInfo");
            methodInfoVariable.InitExpression = new CodeMethodInvokeExpression(
                new CodeTypeOfExpression(typeof(System.Web.Compilation.ExpressionBuilder)), // Refactor
                "GetMethod",
                new CodePrimitiveExpression("GetExpressionBuilder"),
                new CodeBinaryOperatorExpression(new CodeSnippetExpression("System.Reflection.BindingFlags.NonPublic"), CodeBinaryOperatorType.BitwiseOr, new CodeSnippetExpression("System.Reflection.BindingFlags.Static")),
                new CodePrimitiveExpression(null),
                new CodeArrayCreateExpression(typeof(System.Type), new CodeExpression[] { new CodeTypeOfExpression(typeof(System.String)), new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(generatedTypeName), "VirtualPathType") }), // Refactor
                new CodePrimitiveExpression(null));
            CodeVariableDeclarationStatement dynamicMethodVariable = new CodeVariableDeclarationStatement(typeof(System.Reflection.Emit.DynamicMethod), "dynamicMethod");
            dynamicMethodVariable.InitExpression = new CodeObjectCreateExpression(
                typeof(System.Reflection.Emit.DynamicMethod),
                new CodePrimitiveExpression("GetExpressionBuilder"),
                new CodeTypeOfExpression(typeof(System.Web.Compilation.ExpressionBuilder)), // Refactor
                new CodeArrayCreateExpression(typeof(System.Type), new CodeExpression[] { new CodeTypeOfExpression(typeof(System.String)), new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(generatedTypeName), "VirtualPathType") }), // Refactor
                new CodePrimitiveExpression(true));
            CodeVariableDeclarationStatement ilGeneratorVariable = new CodeVariableDeclarationStatement(typeof(System.Reflection.Emit.ILGenerator), "ilGenerator");
            ilGeneratorVariable.InitExpression = new CodeMethodInvokeExpression(
                new CodeVariableReferenceExpression("dynamicMethod"),
                "GetILGenerator");
            CodeMethodInvokeExpression pushParameter1 = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("ilGenerator"), "Emit", new CodeSnippetExpression("System.Reflection.Emit.OpCodes.Ldarg_0"));
            CodeMethodInvokeExpression pushParameter2 = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("ilGenerator"), "Emit", new CodeSnippetExpression("System.Reflection.Emit.OpCodes.Ldarg_1"));
            CodeMethodInvokeExpression callMethodInfo = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("ilGenerator"), "Emit", new CodeSnippetExpression("System.Reflection.Emit.OpCodes.Call"), new CodeVariableReferenceExpression("getExpressionBuilderMethodInfo"));
            CodeMethodInvokeExpression popAndReturn = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("ilGenerator"), "Emit", new CodeSnippetExpression("System.Reflection.Emit.OpCodes.Ret"));
            CodeAssignStatement assignPrivateField = new CodeAssignStatement(
                new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(generatedTypeName), "_getExpressionBuilder"),
                new CodeCastExpression("GetExpressionBuilderMethod", new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("dynamicMethod"), "CreateDelegate", new CodeTypeOfExpression("GetExpressionBuilderMethod"))));

            // Build the conditional.
            CodeConditionStatement ifNull = new CodeConditionStatement();
            ifNull.Condition = privateFieldIsNull;
            ifNull.TrueStatements.Add(methodInfoComment);
            ifNull.TrueStatements.Add(methodInfoVariable);
            ifNull.TrueStatements.Add(dynamicMethodVariable);
            ifNull.TrueStatements.Add(ilGeneratorVariable);
            ifNull.TrueStatements.Add(pushParameter1);
            ifNull.TrueStatements.Add(pushParameter2);
            ifNull.TrueStatements.Add(callMethodInfo);
            ifNull.TrueStatements.Add(popAndReturn);
            ifNull.TrueStatements.Add(assignPrivateField);

            CodeMethodReturnStatement returnPrivateField = new CodeMethodReturnStatement(privateField);

            property.GetStatements.Add(ifNull);
            property.GetStatements.Add(returnPrivateField);

            return property;
        }
        */


        protected virtual CodeExpression CreateCodeExpressionCodeExpression(string prefix, string expression)
        {
            // 1.) Create and initialize a ExpressionBuilderContext object.
            ExpressionBuilderContext builderContext = new ExpressionBuilderContext(base.VirtualPath);

            // 2.) Get the appropriate ExpressionBuilder object for the parsed expression.
            System.Web.Compilation.ExpressionBuilder builder = GetExpressionBuilder(prefix, VirtualPathObj);

            // 3.) Create and initialize a BoundPropertyEntry object.
            BoundPropertyEntry propertyEntry = CreateBoundPropertyEntry();
            propertyEntry.Expression = expression;
            propertyEntry.ExpressionBuilder = builder;
            propertyEntry.ExpressionPrefix = prefix;

            // 4.) Call the public "ParseExpression" method and retain the "parsedData" object it returns.
            object parsedData = builder.ParseExpression(expression, propertyEntry.GetType(), builderContext);

            // 5.) Call the "GetCodeExpression" method of the ExpressionBuilder object and return its value.
            return builder.GetCodeExpression(propertyEntry, parsedData, builderContext);
        }


        protected virtual void ProcessRenderMode(JavaScriptCodeDom codeDom, ScriptType scriptType, out CodeTypeDeclaration handlerClass)
        {
            handlerClass = null;

            switch (scriptType)
            {
                case ScriptType.Inline:



                    break;

                case ScriptType.File:

                    // In the contructor of JavaScriptBuildProvider, check for the RenderMode attribute value. If 
                    // RenderMode is either "File" or "Resource", programmatically inject an HTTP Handler mapped 
                    // to the ScriptName + ".js" file name and to the generated type. The type will then be made 
                    // to implement IHttpHandler and will server out the file when the file name is requested. The 
                    // script block generated will simply reference ScriptName + ".js" as the value of its "src" 
                    // attribute. 

                    // Lou, think of another way to do this so that ScriptResource.axd can grab these files, along 
                    // with any other HTTP Handler or HTTP Module that may want to intercept it.

                    handlerClass = CreateFileHttpHandlerClass(codeDom);

                    break;

                case ScriptType.Embedded:

                    handlerClass = CreateEmbeddedHttpHandlerClass(codeDom);

                    break;
            }
        }


        protected virtual CodeTypeDeclaration CreateFileHttpHandlerClass(JavaScriptCodeDom codeDom)
        {
            CodeTypeReference iHttpHandlerType = new CodeTypeReference(typeof(System.Web.IHttpHandler));

            CodeTypeDeclaration handlerClass = new CodeTypeDeclaration();

            handlerClass.Name = FileHandlerTypeName;
            handlerClass.IsClass = true;
            handlerClass.IsPartial = true;
            handlerClass.TypeAttributes = TypeAttributes.Public;

            handlerClass.BaseTypes.Add(iHttpHandlerType);
            handlerClass.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(System.Runtime.CompilerServices.CompilerGlobalScopeAttribute))));


            // Create and add a CodeDOM representation of an implementation of the "IsReusable" property.
            CodeMemberProperty isReusableProperty = new CodeMemberProperty();

            isReusableProperty.Name = "IsReusable";
            isReusableProperty.Attributes = MemberAttributes.Public;
            isReusableProperty.HasGet = true;
            isReusableProperty.HasSet = false;
            isReusableProperty.Type = new CodeTypeReference(typeof(System.Boolean));
            isReusableProperty.ImplementationTypes.Add(iHttpHandlerType);

            isReusableProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(true)));

            handlerClass.Members.Add(isReusableProperty);


            // Create and add a CodeDOM representation of an implementation of the "ProcessRequest" method.
            CodeMemberMethod processRequestMethod = new CodeMemberMethod();
            
            processRequestMethod.Name = "ProcessRequest";
            processRequestMethod.ReturnType = new CodeTypeReference(typeof(void));
            processRequestMethod.Attributes = MemberAttributes.Public;
            processRequestMethod.ImplementationTypes.Add(iHttpHandlerType);
            
            processRequestMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(System.Web.HttpContext)), "context"));

            CodeTypeReference jsxScriptType = new CodeTypeReference(typeof(JsxScript));
            
            CodeVariableDeclarationStatement newJsxScript = new CodeVariableDeclarationStatement(jsxScriptType, "jsxScript");
            newJsxScript.InitExpression = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(JsxScriptRegistry)), "GetScript", new CodePrimitiveExpression(codeDom.ScriptName));
            processRequestMethod.Statements.Add(newJsxScript);

            CodeVariableReferenceExpression jsxScript = new CodeVariableReferenceExpression("jsxScript");

            CodeBinaryOperatorExpression left = new CodeBinaryOperatorExpression(jsxScript, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
            CodeBinaryOperatorExpression right = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(jsxScript, "Instance"), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));

            CodeConditionStatement conditional = new CodeConditionStatement();
            conditional.Condition = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BooleanAnd, right);
            
            
            CodeVariableDeclarationStatement scriptContent = new CodeVariableDeclarationStatement(new CodeTypeReference(typeof(string)), "scriptContent");
            scriptContent.InitExpression = new CodeMethodInvokeExpression(new CodeMethodInvokeExpression(new CodePropertyReferenceExpression(jsxScript, "Instance"), "BuildScript"), "ToString");
            conditional.TrueStatements.Add(scriptContent);

            conditional.TrueStatements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("context"), "Response"), "ContentType"), new CodePrimitiveExpression("application/x-javascript")));
            conditional.TrueStatements.Add(new CodeMethodInvokeExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("context"), "Response"), "Write", new CodeVariableReferenceExpression("scriptContent")));

            processRequestMethod.Statements.Add(conditional);


            handlerClass.Members.Add(processRequestMethod);


            return handlerClass;
        }


        protected virtual CodeTypeDeclaration CreateEmbeddedHttpHandlerClass(JavaScriptCodeDom codeDom)
        {
            CodeTypeDeclaration handlerClass = new CodeTypeDeclaration();
            handlerClass.Name = codeDom.ScriptName + "EmbeddedHandler";

            handlerClass.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(System.Runtime.CompilerServices.CompilerGlobalScopeAttribute))));

            return handlerClass;
        }
        
        
        public virtual string DetermineReferencedTypeName()
        {
            int indexOfLastPeriod = Parser.ReferencedTypeName.LastIndexOf('.');

            if (indexOfLastPeriod != -1)
            {
                return Parser.ReferencedTypeName.Substring(indexOfLastPeriod + 1);
            }

            return Parser.ReferencedTypeName;
        }


        public virtual string DetermineReferencedTypeNamespace()
        {
            int indexOfLastPeriod = Parser.ReferencedTypeName.LastIndexOf('.');

            if (indexOfLastPeriod > 0)
            {
                return Parser.ReferencedTypeName.Substring(0, indexOfLastPeriod);
            }

            return String.Empty;
        }


        public override Type GetGeneratedType(CompilerResults results)
        {
            Type generatedType = results.CompiledAssembly.GetType(GeneratedFullTypeName);
            
            if (IsInheritingFromTemplateControl)
            {
                object inheritedMarkupVirtualPathObj = CreateVirtualPath(InheritedMarkupVirtualPath);
                string cacheKey = GetInternalCacheKey(inheritedMarkupVirtualPathObj);

                BuildManager buildManager = (BuildManager)typeof(BuildManager).GetField("_theBuildManager", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);

                Type buildResultType = Type.GetType(Properties.Settings.Default.BuildResult_TypeName, true);
                Type buildResultCompiledTemplateTypeType = Type.GetType(Properties.Settings.Default.BuildResultCompiledTemplateType_TypeName, true);
                object buildResult = buildResultCompiledTemplateTypeType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(Type) }, null).Invoke(new object[] { generatedType });
                buildResultType.GetField("_virtualPath", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(buildResult, inheritedMarkupVirtualPathObj);

                buildResultType.GetField("_virtualPathDependencies", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(buildResult, VirtualPathDependencies);
                //buildResultType.GetField("_virtualPathDependencies", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(buildResult, new ArrayList(new string[] { base.VirtualPath }));

                //RemoveDependencyAssemblies();
                MethodInfo cacheBuildResultInternalMethodInfo = typeof(BuildManager).GetMethod("CacheBuildResult", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(string), buildResultType, typeof(DateTime) }, null);

                cacheBuildResultInternalMethodInfo.Invoke(null, new object[] { cacheKey, buildResult, DateTime.UtcNow });
            }
            
            return generatedType;
        }


        public override BuildProviderResultFlags GetResultFlags(CompilerResults results)
        {
            return BuildProviderResultFlags.ShutdownAppDomainOnChange;
        }


        protected virtual void RemoveDependencyAssemblies()
        {
            string cacheKey = GetInternalCacheKey(InheritedMarkupVirtualPath);
            object buildResult = GetBuildResultFromInternalCache(cacheKey);

            if (buildResult != null)
            {
                object memoryCache = typeof(BuildManager).GetField("_memoryCache", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(TheBuildManager);

                if (memoryCache != null)
                {
                    Type memoryCacheType = Type.GetType(Properties.Settings.Default.MemoryBuildResultCache_TypeName, true);
                    Type cacheInternalType = Type.GetType(Properties.Settings.Default.CacheInternal_TypeName, true);
                    Type cacheKeyType = Type.GetType(Properties.Settings.Default.CacheKey_TypeName, true);
                    Type buildResultCompiledAssemblyType = Type.GetType(Properties.Settings.Default.BuildResultCompiledAssemblyBase_TypeName, true);

                    MethodInfo removeAssemblyAndCleanupDependenciesMethodInfo = memoryCacheType.GetMethod("RemoveAssemblyAndCleanupDependencies", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { buildResultCompiledAssemblyType }, null);

                    if (removeAssemblyAndCleanupDependenciesMethodInfo != null)
                    {
                        removeAssemblyAndCleanupDependenciesMethodInfo.Invoke(memoryCache, new object[] { CastBuildResultAsBuildResultCompiledAssemblyBase(buildResult) });
                    }
                }
            }
        }


        protected virtual object GetBuildResultFromInternalCache(string cacheKey)
        {
            Type buildResultType = Type.GetType(Properties.Settings.Default.BuildResult_TypeName, true);
            object buildResult = typeof(BuildManager).GetMethod("GetBuildResultFromCache", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null).Invoke(null, new object[] { cacheKey });

            return buildResult;
        }


        protected virtual string GetInternalCacheKey(string virtualPath)
        {
            object virtualPathObj = CreateVirtualPath(virtualPath);

            bool keyFromVPP = false;
            MethodInfo getCacheKeyFromVirtualPathMethodInfo = typeof(BuildManager).GetMethod("GetCacheKeyFromVirtualPath", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { VirtualPathType, typeof(bool).MakeByRefType() }, null);
            return (string)getCacheKeyFromVirtualPathMethodInfo.Invoke(null, new object[] { virtualPathObj, keyFromVPP });
        }


        protected virtual string GetInternalCacheKey(object virtualPathObj)
        {
            bool keyFromVPP = false;
            MethodInfo getCacheKeyFromVirtualPathMethodInfo = typeof(BuildManager).GetMethod("GetCacheKeyFromVirtualPath", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { VirtualPathType, typeof(bool).MakeByRefType() }, null);
            return (string)getCacheKeyFromVirtualPathMethodInfo.Invoke(null, new object[] { virtualPathObj, keyFromVPP });
        }
        //------\\ Methods //-----------------------------------------------//
    }
}